/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ch.lab24.android.rtm;

import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.TimeZone;

import ch.lab24.android.rtm.provider.RTM;


public class TaskListByDayAdapter extends BaseAdapter {
    private static final int TYPE_DAY = 0;
    private static final int TYPE_MEETING = 1;
    private static final int TYPE_LAST = 2;

    private final TaskListAdapter mAgendaAdapter;
    private final LayoutInflater mInflater;
    private ArrayList<RowInfo> mRowInfo;
    private int mTodayJulianDay;

    public TaskListByDayAdapter(Context context, TaskListAdapter agendaAdapter) {
        mAgendaAdapter = agendaAdapter;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    
    public int getCount() {
        if (mRowInfo != null) {
            return mRowInfo.size();
        }
        return mAgendaAdapter.getCount();
    }

    public Object getItem(int position) {
        if (mRowInfo != null) {
            RowInfo row = mRowInfo.get(position);
            if (row.mType == TYPE_DAY) {
                return row;
            } else {
                return mAgendaAdapter.getItem(row.mData);
            }
        }
        return mAgendaAdapter.getItem(position);
    }

    public long getItemId(int position) {
        if (mRowInfo != null) {
            RowInfo row = mRowInfo.get(position);
            if (row.mType == TYPE_DAY) {
                return position;
            } else {
                return mAgendaAdapter.getItemId(row.mData);
            }
        }
        return mAgendaAdapter.getItemId(position);
    }

    @Override
    public int getViewTypeCount() {
        return TYPE_LAST;
    }

    @Override
    public int getItemViewType(int position) {
        return mRowInfo != null && mRowInfo.size() > position ?
                mRowInfo.get(position).mType : TYPE_DAY;
    }

    private static class ViewHolder {
        TextView dateView;
        TextView dayOfWeekView;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        if ((mRowInfo == null) || (position > mRowInfo.size())) {
            // If we have no row info, mAgendaAdapter returns the view.
            return mAgendaAdapter.getView(position, convertView, parent);
        }

        RowInfo row = mRowInfo.get(position);
        if (row.mType == TYPE_DAY) {
            ViewHolder holder;
            View agendaDayView;
            if ((convertView == null) || (convertView.getTag() == null)) {
                // Create a new AgendaView with a ViewHolder for fast access to
                // views w/o calling findViewById()
                holder = new ViewHolder();
                agendaDayView = mInflater.inflate(R.layout.tasklist_day, parent, false);
                holder.dateView = (TextView) agendaDayView.findViewById(R.id.date);
                holder.dayOfWeekView = (TextView) agendaDayView.findViewById(R.id.day_of_week);
                agendaDayView.setTag(holder);
            } else {
                agendaDayView = convertView;
                holder = (ViewHolder) convertView.getTag();
            }

            // Re-use the member variable "mTime" which is set to the local timezone.
            Date date2 = new Date(setJulianDay(row.mData));
            if (date2.getTime() < RememberTheMilk.NULL_DATE)
            {
	            final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
	            format.setTimeZone(TimeZone.getDefault());
	            holder.dateView.setText(format.format(date2));
	
	            if (row.mData == mTodayJulianDay) {
	                holder.dayOfWeekView.setText(R.string.agenda_today);
	            } else {
	            	SimpleDateFormat df = new SimpleDateFormat("EEEE");
	            	df.setTimeZone(TimeZone.getDefault());
	            	holder.dayOfWeekView.setText(df.format(date2));
	            }
            }
            else
            {
            	holder.dateView.setText(R.string.agenda_nodate);
            	holder.dayOfWeekView.setText("");
            }
            return agendaDayView;
        } else if (row.mType == TYPE_MEETING) {
            return mAgendaAdapter.getView(row.mData, convertView, parent);
        } else {
            // Error
            throw new IllegalStateException("Unknown event type:" + row.mType);
        }
    }

    public void clearDayHeaderInfo() {
        mRowInfo = null;
    }

    public void calculateDays(Cursor cursor) {
        ArrayList<RowInfo> rowInfo = new ArrayList<RowInfo>();
        int prevStartDay = -1;
        TimeZone tz = TimeZone.getDefault();
        Date now = new Date();
        mTodayJulianDay = getJulianDay(now.getTime(), tz.getOffset(now.getTime()));
        
        for (int position = 0; cursor.moveToNext(); position++) {
            long date = cursor.getLong(cursor.getColumnIndex(RTM.Tasks.DUE));
            int startDay = getJulianDay(date, tz.getOffset(date));

            if (startDay != prevStartDay) {
                rowInfo.add(new RowInfo(TYPE_DAY, startDay, date));
                prevStartDay = startDay;
            }

            // Add in the event for this cursor position
            rowInfo.add(new RowInfo(TYPE_MEETING, position, 0));

        }

        mRowInfo = rowInfo;
    }

    private static class RowInfo {
        // mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
        final int mType;

        // If mType is TYPE_DAY, then mData is the Julian day.  Otherwise
        // mType is TYPE_MEETING and mData is the cursor position.
        final int mData;
        
        final long mDate;
        
        RowInfo(int type, int data, long date) {
            mType = type;
            mData = data;
            mDate = date;
        }
    }



    /**
     * Finds the Julian day containing the event at the given position.
     *
     * @param position the list position of an event
     * @return the Julian day containing that event
     */
    public int findJulianDayFromPosition(int position) {
        if (mRowInfo == null || position < 0) {
            return 0;
        }

        int len = mRowInfo.size();
        if (position >= len) return 0;  // no row info at this position

        for (int index = position; index >= 0; index--) {
            RowInfo row = mRowInfo.get(index);
            if (row.mType == TYPE_DAY) {
                return row.mData;
            }
        }
        return 0;
    }

    /**
     * Converts a list position to a cursor position.  The list contains
     * day headers as well as events.  The cursor contains only events.
     *
     * @param listPos the list position of an event
     * @return the corresponding cursor position of that event
     */
    public int getCursorPosition(int listPos) {
        if (mRowInfo != null && listPos >= 0) {
            RowInfo row = mRowInfo.get(listPos);
            if (row.mType == TYPE_MEETING) {
                return row.mData;
            }
        }
        return listPos;
    }

    @Override
    public boolean areAllItemsEnabled() {
        return false;
    }

    @Override
    public boolean isEnabled(int position) {
        if (mRowInfo != null && position < mRowInfo.size()) {
            RowInfo row = mRowInfo.get(position);
            return row.mType == TYPE_MEETING;
        }
        return true;
    }

    /**
     * Searches for the day that matches the given Time object and returns the
     * list position of that day.  If there are no events for that day, then it
     * finds the nearest day (before or after) that has events and returns the
     * list position for that day.
     *
     * @param time the date to search for
     * @return the cursor position of the first event for that date, or zero
     * if no match was found
     */
    public int findDayPositionNearestTime(Date time) {
        if (mRowInfo == null) {
            return 0;
        }
        long millis = time.getTime();
        TimeZone tz = TimeZone.getDefault();        
        int julianDay = getJulianDay(millis, tz.getOffset(time.getTime()));
        int minDistance = 1000;  // some big number
        int minIndex = 0;
        int len = mRowInfo.size();
        for (int index = 0; index < len; index++) {
            RowInfo row = mRowInfo.get(index);
            if (row.mType == TYPE_DAY) {
                int distance = Math.abs(julianDay - row.mData);
                if (distance == 0) {
                    return index;
                }
                if (distance < minDistance) {
                    minDistance = distance;
                    minIndex = index;
                }
            }
        }

        // We didn't find an exact match so take the nearest day that had
        // events.
        return minIndex;
    }
    
    /**
     * The Julian day of the epoch, that is, January 1, 1970 on the Gregorian
     * calendar.
     */
    public static final long EPOCH_JULIAN_DAY = 0; //2440588;

    
    private int getJulianDay(long millis, long gmtoff) {
        long offsetMillis = gmtoff;
        long julianDay = (millis + offsetMillis) / (1000 * 60 * 60 * 24);
        return (int) julianDay + (int) EPOCH_JULIAN_DAY;
    }
    
    public long setJulianDay(int julianDay) {
        long millis = (julianDay - EPOCH_JULIAN_DAY) * (1000 * 60 * 60 * 24);        
        return millis;
    }

}

